/*     */ package com.zimbra.cs.imap;
/*     */ 
/*     */ import com.google.common.collect.ImmutableSet;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.AuthToken;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.Server;
/*     */ import com.zimbra.cs.mailclient.MailConfig;
/*     */ import com.zimbra.cs.mailclient.MailConfig.Security;
/*     */ import com.zimbra.cs.mailclient.MailInputStream;
/*     */ import com.zimbra.cs.mailclient.auth.Authenticator;
/*     */ import com.zimbra.cs.mailclient.auth.AuthenticatorFactory;
/*     */ import com.zimbra.cs.mailclient.imap.IDInfo;
/*     */ import com.zimbra.cs.mailclient.imap.ImapConnection;
/*     */ import com.zimbra.cs.service.AuthProvider;
/*     */ import com.zimbra.cs.util.BuildInfo;
/*     */ import java.io.ByteArrayOutputStream;
/*     */ import java.io.IOException;
/*     */ import java.io.OutputStream;
/*     */ import java.io.UnsupportedEncodingException;
/*     */ import java.net.InetSocketAddress;
/*     */ import java.util.Set;
/*     */ import javax.security.auth.login.LoginException;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ final class ImapProxy
/*     */ {
/*  48 */   private static final Set<String> UNSTRUCTURED_CODES = ImmutableSet.of("OK", "NO", "BAD", "PREAUTH", "BYE");
/*  49 */   private static final AuthenticatorFactory AUTH_FACTORY = new AuthenticatorFactory();
/*     */   
/*  51 */   static { AUTH_FACTORY.register("X-ZIMBRA", ZimbraClientAuthenticator.class); }
/*     */   
/*     */ 
/*     */ 
/*     */   private final ImapHandler handler;
/*     */   
/*     */   ImapProxy(ImapHandler handler, ImapPath path)
/*     */     throws ServiceException
/*     */   {
/*  60 */     this.handler = handler;
/*  61 */     this.path = path;
/*     */     
/*  63 */     Account acct = handler.getCredentials().getAccount();
/*  64 */     if (acct == null) {
/*  65 */       throw ServiceException.PROXY_ERROR(new Exception("no such authenticated user"), path.asImapPath());
/*     */     }
/*  67 */     Account pathOwner = path.getOwnerAccount();
/*  68 */     if (pathOwner == null) {
/*  69 */       throw ServiceException.PROXY_ERROR(new Exception("no such account"), path.asImapPath());
/*     */     }
/*  71 */     Server server = Provisioning.getInstance().getServer(pathOwner);
/*  72 */     String host = server.getServiceHostname();
/*  73 */     com.zimbra.cs.mailclient.imap.ImapConfig config = new com.zimbra.cs.mailclient.imap.ImapConfig();
/*  74 */     config.setAuthenticationId(acct.getName());
/*  75 */     config.setMechanism("X-ZIMBRA");
/*  76 */     config.setAuthenticatorFactory(AUTH_FACTORY);
/*  77 */     config.setReadTimeout(LC.javamail_imap_timeout.intValue());
/*  78 */     config.setConnectTimeout(config.getReadTimeout());
/*  79 */     config.setHost(host);
/*  80 */     if (server.isImapServerEnabled()) {
/*  81 */       config.setPort(server.getIntAttr("zimbraImapBindPort", 143));
/*  82 */     } else if (server.isImapSSLServerEnabled()) {
/*  83 */       config.setPort(server.getIntAttr("zimbraImapSSLBindPort", 993));
/*  84 */       config.setSecurity(MailConfig.Security.SSL);
/*     */     } else {
/*  86 */       throw ServiceException.PROXY_ERROR(new Exception("no open IMAP port for server " + host), path.asImapPath());
/*     */     }
/*     */     
/*  89 */     ZimbraLog.imap.info("opening proxy connection (user=%s, host=%s, path=%s)", new Object[] { acct.getName(), host, path.getReferent().asImapPath() });
/*     */     
/*  91 */     this.connection = new ImapConnection(config);
/*     */     try {
/*  93 */       this.connection.connect();
/*  94 */       this.connection.id(createIDInfo(handler));
/*  95 */       this.connection.authenticate(AuthProvider.getAuthToken(acct).getEncoded());
/*     */     } catch (Exception e) {
/*  97 */       dropConnection();
/*  98 */       throw ServiceException.PROXY_ERROR(e, null);
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   ImapProxy(InetSocketAddress remote, String username, String password, ImapHandler handler)
/*     */     throws IOException, LoginException
/*     */   {
/* 107 */     this.handler = handler;
/* 108 */     this.path = null;
/* 109 */     com.zimbra.cs.mailclient.imap.ImapConfig config = new com.zimbra.cs.mailclient.imap.ImapConfig();
/* 110 */     config.setAuthenticationId(username);
/* 111 */     config.setMechanism("X-ZIMBRA");
/* 112 */     config.setAuthenticatorFactory(AUTH_FACTORY);
/* 113 */     config.setHost(remote.getHostName());
/* 114 */     config.setPort(remote.getPort());
/* 115 */     this.connection = new ImapConnection(config);
/* 116 */     this.connection.connect();
/* 117 */     this.connection.id(createIDInfo(handler));
/* 118 */     this.connection.authenticate(password);
/*     */   }
/*     */   
/*     */   private IDInfo createIDInfo(ImapHandler handler) {
/* 122 */     IDInfo id = new IDInfo();
/* 123 */     id.put("name", "ZCS");
/* 124 */     id.put("version", BuildInfo.VERSION);
/* 125 */     id.put("X-VIA", handler.getNextVia());
/* 126 */     if (handler.getOrigRemoteIp() != null) {
/* 127 */       id.put("X-ORIGINATING-IP", handler.getOrigRemoteIp());
/*     */     }
/* 129 */     return id;
/*     */   }
/*     */   
/*     */   ImapPath getPath() {
/* 133 */     return this.path;
/*     */   }
/*     */   
/*     */   void dropConnection() {
/* 137 */     ImapConnection conn = this.connection;
/* 138 */     this.connection = null;
/* 139 */     if (conn == null) {
/* 140 */       return;
/*     */     }
/*     */     
/* 143 */     ZimbraLog.imap.info("closing proxy connection");
/* 144 */     conn.close();
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   private final ImapPath path;
/*     */   
/*     */ 
/*     */   private ImapConnection connection;
/*     */   
/*     */   private Thread idleThread;
/*     */   
/*     */   boolean select(String tag, byte params, QResyncInfo qri)
/*     */     throws ImapProxyException, ServiceException
/*     */   {
/* 159 */     String command = (params & 0x1) == 0 ? "SELECT" : "EXAMINE";
/* 160 */     StringBuilder select = new StringBuilder(100);
/* 161 */     select.append(tag).append(' ').append(command).append(' ');
/* 162 */     select.append(this.path.getReferent().asUtf7String());
/* 163 */     if ((params & 0x2) != 0) {
/* 164 */       select.append(" (");
/* 165 */       if (qri == null) {
/* 166 */         select.append("CONDSTORE");
/*     */       } else {
/* 168 */         select.append("QRESYNC (").append(qri.uvv).append(' ').append(qri.modseq);
/* 169 */         if (qri.knownUIDs != null)
/* 170 */           select.append(' ').append(qri.knownUIDs);
/* 171 */         if (qri.seqMilestones != null)
/* 172 */           select.append(" (").append(qri.seqMilestones).append(' ').append(qri.uidMilestones).append(')');
/* 173 */         select.append(')');
/*     */       }
/* 175 */       select.append(')');
/*     */     }
/*     */     
/* 178 */     return proxyCommand("\r\n".getBytes(), true, false);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   boolean idle(ImapRequest req, boolean begin)
/*     */     throws ImapProxyException, IOException
/*     */   {
/* 191 */     if (!begin)
/*     */     {
/* 193 */       if (this.handler == null) {
/* 194 */         throw new ImapProxyException("client connection already closed");
/*     */       }
/* 196 */       Thread idle = this.idleThread;
/* 197 */       if (idle == null) {
/* 198 */         throw new ImapProxyException("bad proxy state: no IDLE thread active when attempting DONE");
/*     */       }
/*     */       
/* 201 */       writeRequest(req.toByteArray());
/*     */       
/* 203 */       this.idleThread = null;
/*     */       try {
/* 205 */         idle.join(5000L);
/*     */       } catch (InterruptedException ie) {}
/* 207 */       if (idle.isAlive())
/* 208 */         this.handler.dropConnection(false);
/*     */     } else {
/* 210 */       final ImapConnection conn = this.connection;
/* 211 */       if (conn == null) {
/* 212 */         throw new ImapProxyException("proxy connection already closed");
/*     */       }
/* 214 */       com.zimbra.cs.mailclient.imap.ImapConfig config = conn.getImapConfig();
/* 215 */       final int oldTimeout = config != null ? config.getReadTimeout() : LC.javamail_imap_timeout.intValue();
/*     */       
/* 217 */       final byte[] payload = req.toByteArray();
/*     */       
/* 219 */       this.idleThread = new Thread()
/*     */       {
/*     */         public void run() {
/* 222 */           boolean success = false;
/*     */           try
/*     */           {
/* 225 */             conn.setReadTimeout(ImapProxy.this.handler.getConfig().getAuthenticatedMaxIdleTime());
/*     */             
/* 227 */             boolean ok = ImapProxy.this.proxyCommand(payload, true, true);
/*     */             
/* 229 */             conn.setReadTimeout(oldTimeout);
/*     */             
/* 231 */             success = ok;
/*     */           } catch (IOException e) {
/* 233 */             ZimbraLog.imap.warn("error encountered during IDLE; dropping connection", e);
/*     */           }
/* 235 */           if (!success) {
/* 236 */             ImapProxy.this.handler.dropConnection(true);
/*     */           }
/*     */         }
/* 239 */       };
/* 240 */       this.idleThread.setName("Imap-Idle-Proxy-" + Thread.currentThread().getName());
/* 241 */       this.idleThread.start();
/*     */     }
/* 243 */     return true;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   boolean proxy(ImapRequest req)
/*     */     throws ImapProxyException, IOException
/*     */   {
/* 256 */     proxyCommand(req.toByteArray(), true, false);
/* 257 */     return true;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   boolean proxy(String tag, String command)
/*     */     throws ImapProxyException
/*     */   {
/* 269 */     proxyCommand((tag + ' ' + command + "\r\n").getBytes(), true, false);
/* 270 */     return true;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   void fetchNotifications()
/*     */     throws ImapProxyException
/*     */   {
/* 279 */     String tag = this.connection == null ? "1" : this.connection.newTag();
/* 280 */     proxyCommand((tag + " NOOP\r\n").getBytes(), false, false);
/*     */   }
/*     */   
/*     */   private ImapConnection writeRequest(byte[] payload) throws ImapProxyException {
/* 284 */     ImapConnection conn = this.connection;
/* 285 */     if (conn == null) {
/* 286 */       throw new ImapProxyException("proxy connection already closed");
/*     */     }
/*     */     
/* 289 */     OutputStream remote = conn.getOutputStream();
/* 290 */     if (remote == null) {
/* 291 */       dropConnection();
/* 292 */       throw new ImapProxyException("proxy connection already closed");
/*     */     }
/*     */     try {
/* 295 */       remote.write(payload);
/* 296 */       remote.flush();
/*     */     } catch (IOException e) {
/* 298 */       throw new ImapProxyException(e);
/*     */     }
/* 300 */     return conn;
/*     */   }
/*     */   
/*     */   private boolean proxyCommand(byte[] payload, boolean includeTaggedResponse, boolean isIdle) throws ImapProxyException
/*     */   {
/* 305 */     ImapConnection conn = writeRequest(payload);
/* 306 */     MailInputStream min = conn.getInputStream();
/* 307 */     OutputStream out = this.handler.output;
/* 308 */     if (out == null) {
/* 309 */       dropConnection();
/* 310 */       throw new ImapProxyException("client connection already closed");
/*     */     }
/*     */     
/*     */ 
/* 314 */     boolean success = false;
/*     */     try {
/*     */       int first;
/* 317 */       while ((first = min.peek()) != -1)
/*     */       {
/* 319 */         boolean tagged = (first != 42) && (first != 43);
/* 320 */         boolean structured = first == 42;
/* 321 */         boolean proxy = ((first != 43) || (isIdle)) && ((!tagged) || (includeTaggedResponse));
/*     */         
/* 323 */         ByteArrayOutputStream line = proxy ? new ByteArrayOutputStream() : null;
/* 324 */         StringBuilder debug = (proxy) && (ZimbraLog.imap.isDebugEnabled()) ? new StringBuilder("  pxy: ") : null;
/* 325 */         StringBuilder condition = new StringBuilder(10);
/*     */         
/* 327 */         boolean quoted = false;boolean escaped = false;boolean space1 = false;boolean space2 = false;
/* 328 */         int literal = -1;
/* 329 */         int c; while ((c = min.read()) != -1)
/*     */         {
/* 331 */           if (!space2) {
/* 332 */             if ((c == 32) && (!space1)) {
/* 333 */               space1 = true;
/* 334 */             } else if (c == 32) {
/* 335 */               space2 = true;
/* 336 */               String code = condition.toString().toUpperCase();
/* 337 */               if ("BYE".equals(code)) {
/* 338 */                 dropConnection();
/* 339 */                 throw new ImapProxyException("proxy connection already closed");
/*     */               }
/* 341 */               if (tagged) {
/* 342 */                 success = ("OK".equals(code)) || ((isIdle) && ("BAD".equals(code)));
/*     */               }
/* 344 */               structured &= !UNSTRUCTURED_CODES.contains(code);
/* 345 */             } else if (space1) {
/* 346 */               condition.append((char)c);
/*     */             }
/*     */           }
/*     */           
/*     */ 
/* 351 */           if (structured) {
/* 352 */             if (escaped) {
/* 353 */               escaped = false;
/* 354 */             } else if ((quoted) && (c == 92)) {
/* 355 */               escaped = true;
/* 356 */             } else if (c == 34) {
/* 357 */               quoted = !quoted;
/* 358 */             } else if ((!quoted) && (c == 123)) {
/* 359 */               literal = 0;
/* 360 */             } else if ((literal != -1) && (c >= 48) && (c <= 57)) {
/* 361 */               literal = literal * 10 + (c - 48);
/*     */             }
/*     */           }
/* 364 */           if ((!quoted) && (c == 13) && (min.peek() == 10))
/*     */           {
/* 366 */             min.read();
/*     */             
/* 368 */             if (proxy) {
/* 369 */               out.write(line.toByteArray());
/* 370 */               out.write(ImapHandler.LINE_SEPARATOR_BYTES);
/* 371 */               line.reset();
/* 372 */               if (isIdle) {
/* 373 */                 out.flush();
/*     */               }
/*     */             }
/* 376 */             if (literal == -1) {
/*     */               break;
/*     */             }
/* 379 */             byte[] buffer = literal == 0 ? null : new byte[Math.min(literal, 65536)];
/* 380 */             while (literal > 0) {
/* 381 */               int read = min.read(buffer, 0, Math.min(literal, buffer.length));
/* 382 */               if (read == -1)
/*     */                 break;
/* 384 */               if (proxy)
/* 385 */                 out.write(buffer, 0, read);
/* 386 */               literal -= read;
/*     */             }
/* 388 */             literal = -1;
/* 389 */             if (isIdle)
/* 390 */               out.flush();
/* 391 */           } else if (proxy) {
/* 392 */             line.write(c);
/* 393 */             if (debug != null) {
/* 394 */               debug.append((char)c);
/*     */             }
/*     */           }
/*     */         }
/* 398 */         if (debug != null) {
/* 399 */           ZimbraLog.imap.debug(debug.toString());
/*     */         }
/* 401 */         if (tagged) {
/*     */           break;
/*     */         }
/*     */       }
/* 405 */       out.flush();
/*     */     } catch (ImapProxyException e) {
/* 407 */       throw e;
/*     */     } catch (IOException e) {
/* 409 */       throw new ImapProxyException(e);
/*     */     }
/* 411 */     return success;
/*     */   }
/*     */   
/*     */   public static final class ZimbraClientAuthenticator extends Authenticator {
/*     */     private String username;
/*     */     private String authtoken;
/*     */     private boolean complete;
/*     */     
/*     */     public void init(MailConfig config, String password) {
/* 420 */       this.username = config.getAuthenticationId();this.authtoken = password;
/*     */     }
/*     */     
/* 423 */     public String getMechanism() { return "X-ZIMBRA"; }
/* 424 */     public boolean isComplete() { return this.complete; }
/*     */     
/* 426 */     public boolean hasInitialResponse() { return true; }
/* 427 */     public byte[] getInitialResponse() { return evaluateChallenge(null); }
/*     */     
/*     */     public byte[] evaluateChallenge(byte[] challenge) {
/* 430 */       this.complete = true;
/* 431 */       String response = this.username + '\000' + this.username + '\000' + this.authtoken;
/*     */       try {
/* 433 */         return response.getBytes("utf-8");
/*     */       } catch (UnsupportedEncodingException uee) {}
/* 435 */       return response.getBytes();
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/imap/ImapProxy.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */